package org.example;


import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.net.HttpURLConnection;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class VideoRemoveWatermark {

    /**
     * 解析视频信息
     *
     * @param txt 包含视频链接的文本内容
     * @return 包含解析结果的JSONObject对象，解析失败时返回null
     */
    public JSONObject parseVideo(String txt) {
        JSONObject respJson = new JSONObject();
        try {
            String url = extractUrl(txt);
            if (url == null) {
                return null;
            }

            String redirectUrl = getRedirectUrl(url);
            if (redirectUrl == null) {
                return null;
            }

            Document document = Jsoup.connect(redirectUrl)
                    .userAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1")
                    .get();

            Elements scriptElements = document.select("script");
            for (Element scriptElement : scriptElements) {
                String scriptContent = scriptElement.html();
                if (scriptContent.contains("window._ROUTER_DATA")) {
                    parseRouterData(scriptContent, respJson);
                } else if (scriptContent.contains("window.__INITIAL_STATE__")) {
                    parseInitialState(scriptContent, respJson);
                } else if (scriptContent.contains("window.INIT_STATE")) {
                    parseInitState(scriptContent, respJson);
                }
            }
        } catch (Exception e) {
            return null;
        }
        return respJson.isEmpty() ? null : respJson;
    }

    /**
     * 从给定的字符串消息中提取URL链接
     *
     * @param msg 包含URL的字符串消息
     * @return 提取出的URL字符串，如果没有找到则返回null
     */
    private String extractUrl(String msg) {
        String regex = "(https?://\\S+)";
        Matcher matcher = Pattern.compile(regex).matcher(msg);
        return matcher.find() ? matcher.group(1) : null;
    }

    /**
     * 解析路由数据并提取页面信息
     *
     * @param scriptContent 包含JSON数据的脚本内容字符串
     * @param respJson      用于存储解析结果的响应JSON对象
     */
    private void parseRouterData(String scriptContent, JSONObject respJson) {
        String jsonData = extractJsonData(scriptContent);
        JSONObject jsonObject = JSONUtil.parseObj(jsonData);
        JSONObject loaderDataJson = jsonObject.getJSONObject("loaderData");
        if (loaderDataJson != null) {
            parsePageInfo(loaderDataJson, respJson);
        }
    }

    /**
     * 解析页面信息并根据页面类型调用相应的解析方法
     *
     * @param loaderDataJson 包含页面数据的JSON对象，用于提取视频或笔记页面信息
     * @param respJson       响应JSON对象，用于存储解析后的结果数据
     */
    private void parsePageInfo(JSONObject loaderDataJson, JSONObject respJson) {
        // 提取视频页面信息和笔记页面信息
        JSONObject pageInfoJson = loaderDataJson.getJSONObject("video_(id)/page");
        JSONObject notePageInfoJson = loaderDataJson.getJSONObject("note_(id)/page");

        // 根据页面类型调用对应的解析方法
        if (pageInfoJson != null) {
            parseVideoInfo(pageInfoJson, respJson);
        } else if (notePageInfoJson != null) {
            parseNoteInfo(notePageInfoJson, respJson);
        }
    }

    /**
     * 解析视频信息并填充到响应对象中
     *
     * @param pageInfoJson 包含页面信息的JSON对象，用于提取视频信息
     * @param respJson     响应JSON对象，用于存储解析后的视频信息
     */
    private void parseVideoInfo(JSONObject pageInfoJson, JSONObject respJson) {
        // 提取视频信息对象
        JSONObject videoInfoRes = pageInfoJson.getJSONObject("videoInfoRes");
        if (videoInfoRes != null) {
            // 获取视频列表中的第一个视频项
            JSONObject itemList = videoInfoRes.getJSONArray("item_list").getJSONObject(0);
            populateVideoResponse(itemList, respJson);
        }
    }

    /**
     * 填充视频响应数据
     *
     * @param itemList 包含视频信息的JSON对象
     * @param respJson 用于存储响应数据的JSON对象
     */
    private void populateVideoResponse(JSONObject itemList, JSONObject respJson) {
        // 提取视频描述信息
        String desc = itemList.getStr("desc");
        // 提取作者昵称
        String nickName = itemList.getJSONObject("author").getStr("nickname");
        // 提取视频播放地址，并进行URL格式转换
        String videoUrl = itemList.getJSONObject("video").getJSONObject("play_addr")
                .getJSONArray("url_list").getFirst().toString()
                .replaceAll("playwm", "play")
                .replaceAll("720p", "1080p");

        respJson.set("title", desc);
        respJson.set("nickName", nickName);
        respJson.set("type", 1);
        respJson.set("videoUrl", videoUrl);
    }

    /**
     * 解析笔记信息并填充响应JSON对象
     *
     * @param notePageInfoJson 包含笔记页面信息的JSON对象，用于提取视频和图片信息
     * @param respJson         响应JSON对象，用于存储解析后的笔记信息
     */
    private void parseNoteInfo(JSONObject notePageInfoJson, JSONObject respJson) {
        // 提取视频信息和基本笔记信息
        JSONObject videoInfoRes = notePageInfoJson.getJSONObject("videoInfoRes");
        JSONObject itemList = videoInfoRes.getJSONArray("item_list").getJSONObject(0);
        String desc = itemList.getStr("desc");
        String nickName = itemList.getJSONObject("author").getStr("nickname");

        // 解析图片列表信息
        JSONArray jsonArray = itemList.getJSONArray("images");
        ArrayList<String> imageList = new ArrayList<>();
        for (Object object : jsonArray) {
            JSONObject imageJson = JSONUtil.parseObj(object.toString());
            String urlList = imageJson.getJSONArray("url_list").getFirst().toString();
            imageList.add(urlList);
        }

        // 将解析后的信息填充到响应JSON中
        respJson.set("title", desc);
        respJson.set("nickName", nickName);
        respJson.set("type", 2);
        respJson.set("imageList", imageList);
    }

    /**
     * 解析初始状态数据，从脚本内容中提取笔记信息并填充到响应JSON对象中。
     * <p>
     * 该方法会根据笔记类型（视频或图片）分别处理数据：
     * - 如果是视频类型，则提取视频URL等信息；
     * - 如果是图片类型，则提取图片列表信息。
     * </p>
     *
     * @param scriptContent 包含JSON数据的脚本字符串内容
     * @param respJson      用于存储解析结果的响应JSON对象
     */
    private void parseInitialState(String scriptContent, JSONObject respJson) {
        // 提取并预处理JSON数据，将undefined替换为字符串"undefined"
        String jsonData = extractJsonData(scriptContent).replaceAll("undefined", "\"undefined\"");
        JSONObject jsonObject = JSONUtil.parseObj(jsonData);
        JSONObject noteData = jsonObject.getJSONObject("noteData");
        JSONObject data = noteData.getJSONObject("data");
        JSONObject noteDataJson = data.getJSONObject("noteData");
        String desc = noteDataJson.getStr("desc");
        String type = noteDataJson.getStr("type");
        String nickName = noteDataJson.getJSONObject("user").getStr("nickName");

        // 判断笔记类型是否为视频
        if ("video".equals(type)) {
            // 提取视频相关信息并放入响应JSON中
            String videoUrl = noteDataJson.getJSONObject("video").getJSONObject("media").getJSONObject("stream").getJSONArray("h265").getJSONObject(0).getStr("masterUrl");
            respJson.set("title", desc);
            respJson.set("nickName", nickName);
            respJson.set("type", 1);
            respJson.set("videoUrl", videoUrl);
        } else {
            // 处理图片类型数据
            JSONArray jsonArray = noteDataJson.getJSONArray("imageList");
            ArrayList<String> imageList = new ArrayList<>();
            if (jsonArray.size() == 1) {
                // 单张图片直接获取URL
                String url = jsonArray.getJSONObject(0).getStr("url");
                imageList.add(url);
            } else {
                // 多张图片时尝试获取高质量流地址，若无则使用默认URL
                for (Object object : jsonArray) {
                    JSONObject imageJson = JSONUtil.parseObj(object.toString());
                    JSONObject stream = imageJson.getJSONObject("stream");
                    String urlList = null;
                    if (stream != null && !stream.isEmpty()) {
                        urlList = stream.getJSONArray("h264").getJSONObject(0).getStr("masterUrl");
                    }
                    if (StrUtil.isEmpty(urlList)) {
                        urlList = imageJson.getStr("url");
                    }
                    imageList.add(urlList);
                }
            }
            respJson.set("title", desc);
            respJson.set("nickName", nickName);
            respJson.set("type", 2);
            respJson.set("imageList", imageList);
        }
    }

    /**
     * 快手视频
     *
     * @param respJson      响应JSON对象，用于存储解析结果
     * @param scriptContent 脚本内容字符串，包含需要解析的JSON数据
     */
    private void parseInitState(String scriptContent, JSONObject respJson) {
        // 提取并解析JSON数据
        String jsonData = extractJsonData(scriptContent);
        JSONObject jsonObject = JSONUtil.parseObj(jsonData);
        String prefix = "tusjoh.0sftu0xe0vhI6Bqq0qipup0tjnqmf0jogp@lqo";

        // 根据前缀查找目标JSON对象
        JSONObject object = findJSONObjectByPrefix(jsonObject, prefix);
        if (object != null) {
            // 获取视频基本信息
            JSONObject photo = object.getJSONObject("photo");
            String userName = photo.getStr("userName");
            String caption = photo.getStr("caption");
            JSONArray jsonArray = photo.getJSONArray("mainMvUrls");

            // 判断是否存在视频URL，如果存在则解析视频信息，否则解析图集信息
            if (!jsonArray.isEmpty()) {
                String videoUrl = jsonArray.getJSONObject(0).getStr("url");
                respJson.set("title", caption);
                respJson.set("nickName", userName);
                respJson.set("type", 1);
                respJson.set("videoUrl", videoUrl);
            } else {
                parseAtlasPhoto(photo, respJson, caption, userName);
            }
        }
    }

    /**
     * 快手图集
     *
     * @param photo    包含图集信息的JSON对象
     * @param respJson 用于存储解析结果的响应JSON对象
     * @param caption  图集标题
     * @param userName 用户昵称
     */
    private void parseAtlasPhoto(JSONObject photo, JSONObject respJson, String caption, String userName) {
        // 解析图集信息
        JSONObject atlasJson = photo.getJSONObject("ext_params").getJSONObject("atlas");
        String http = atlasJson.getJSONArray("cdn").getFirst().toString();
        JSONArray atlasJsonJSONArray = atlasJson.getJSONArray("list");

        // 构建完整的图片URL列表
        ArrayList<String> imageList = new ArrayList<>();
        for (Object o : atlasJsonJSONArray) {
            String imagUrl = "https://" + http + o;
            imageList.add(imagUrl);
        }

        // 设置响应数据
        respJson.set("title", caption);
        respJson.set("nickName", userName);
        respJson.set("type", 2);
        respJson.set("imageList", imageList);
    }

    private String extractJsonData(String scriptContent) {
        return scriptContent.substring(scriptContent.indexOf("{"), scriptContent.lastIndexOf("}") + 1);
    }

    private static JSONObject findJSONObjectByPrefix(JSONObject jsonObject, String prefix) {
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            if (entry.getKey().startsWith(prefix)) {
                return (JSONObject) entry.getValue();
            }
        }
        return null;
    }

    /**
     * 获取重定向URL地址
     * 该函数通过发送HTTP请求获取指定URL的重定向地址，主要用于处理301/302重定向情况
     *
     * @param redirectUrl 原始URL地址，需要检查是否发生重定向
     * @return 返回重定向后的URL地址，如果没有重定向则返回null
     */
    public String getRedirectUrl(String redirectUrl) {
        try {
            // 设置请求User-Agent，模拟iPhone浏览器访问
            String ua = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1";
            // 禁止自动跟随重定向
            HttpClient client = HttpClient.newBuilder()
                    .followRedirects(HttpClient.Redirect.NEVER)
                    .build();

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(redirectUrl))
                    .header(Header.USER_AGENT.getValue(), ua)
                    .GET()
                    .build();

            // 发送请求并获取响应
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            // 获取响应码
            int responseCode = response.statusCode();

            // 判断是否为临时重定向(302)或永久重定向(301)
            if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_MOVED_PERM) {
                // 返回重定向地址
                return response.headers().firstValue("Location").orElse(null);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}